package ca.krakenpower.kvoxels;

public class Vector3 {
	
	public static final Vector3 ZERO = 	get(0, 0, 0);
	public static final Vector3 UP =	get(0, 1, 0);
	public static final Vector3 DOWN =	get(0, -1, 0);
	public static final Vector3 RIGHT = get(1, 0, 0);
	public static final Vector3 LEFT =	get(-1, 0, 0);
	public static final Vector3 FWD =	get(0, 0, 1);
	public static final Vector3 BACK =	get(0, 0, -1);
	
	public final double x, y, z;
	
	private Vector3(double x, double y, double z) {
		this.x = x;
		this.y = y;
		this.z = z;
	}
	
	public Vector3 scale(double s) {
		return get(s * x, s * y, s * z);
	}
	
	public Vector3 normalize() {
		return this.scale(1 / length());
	}
	
	public Vector3 rotate(double angle, Vector3 axis) {
		if (axis == null) {
			throw new IllegalArgumentException("null vector given as rotation axis");
		}
		
		// pp = qpq*
		
		Quaternion q = Quaternion.get(angle, axis);
		Quaternion p = Quaternion.get(0, x, y, z);
		Quaternion qs = q.conjugate();
		Quaternion pp = q.product(p).product(qs);
		
		return pp.vectorPart();
	}
	
	/**
	 * @return a vector with the same magnitude as this vector but opposite direction.
	 */
	public Vector3 negate() {
		return get(0 - x, 0 - y, 0 - z);
	}
	
	/**
	 * @param b
	 * @return a vector who's components are the sums of the respective components of this vector and b.
	 */
	public Vector3 sum(Vector3 b) {
		if (b == null) {
			throw new IllegalArgumentException("null vector given as summand");
		}
		
		return get(x + b.x, y + b.y, z + b.z);
	}
	
	/**
	 * @param b
	 * @return the vector cross product of this vector and vector b.
	 */
	public Vector3 cross(Vector3 b) {
		if (b == null) {
			throw new IllegalArgumentException("null vector given as argument of cross product");
		}
		
		double s1, s2, s3;
		s1 = (this.y * b.z) - (this.z * b.y);
		s2 = (this.z * b.x) - (this.x * b.z);
		s3 = (this.x * b.y) - (this.y * b.x);
		
		return get(s1, s2, s3);
	}
	
	/**
	 * @param b
	 * @return the scalar dot product of this vector and vector b.
	 */
	public double dot(Vector3 b) {
		if (b == null) {
			throw new IllegalArgumentException("null vector given as argument of dot product");
		}
		
		return (this.x * b.x) + (this.y * b.y) + (this.z * b.z); 
	}
	
	/**
	 * @return the length (magnitude) of this vector.
	 */
	public double length() {
		return Math.sqrt((x * x) + (y * y) + (z * z));
	}
	
	/**
	 * @param b
	 * @return the angle (in radians) formed by this vector and vector b.
	 */
	public double angle(Vector3 b) {
		return Math.acos(this.dot(b) / (this.length() * b.length()));
	}
	
	/**
	 * @param x
	 * @param y
	 * @param z
	 * @return a new vector with the supplied components
	 */
	public static Vector3 get(double x, double y, double z) {
		return new Vector3(x, y, z);
	}
	
	@Override
	public String toString() {
		return "[" + x + ", " + y + ", " + z + "]";
	}

}
